#pragma once
#include "core/CList.h"
#include "Entity.h"
#include "Global.h"
#include "Cache.h"
#include "GLState.h"

class Scene {
public:
	static void init(IGLProcLoader* procLoader);

	static bool isInitialized() { return s_isInitialized; }

	static void switchRenderingStatus(bool on) {
		Cache::setReceivingData(on);
		s_isRendering = on;
	}

	static bool isRendering() { return s_isRendering; }

	static void add(Entity* entity) {
		if (!entity) return;
		s_entities << entity;
	}

	/**
	 * @warning `delete` result in calling destructor synchronously!
	 *           destructor will execuate at the same thread as `delete` calls.
	 */
	static void remove(Entity* entity) {
		if (entity && s_entities.removeOne(entity)) {
			if (entity->bornThread() != std::this_thread::get_id())
				LogEE << "delete entity in a wrong thread!!!!!! entity_type:" << static_cast<int>(entity->type());
			delete entity;
		}
	}

	static const CList<Entity*>& entities() { return s_entities; }

	static void renderZhiche(Entity* zhiche);

	// render to the back buffer --> swap buffers manually --> display the front buffer
	static FBO* update() {
		Global::increaseFrameCnt();
		GLState::bindBgTexture(Global::isDarkMode());
		GLState::bindAndClear(s_backFBO);

		s_entities.sort([](Entity* e1, Entity* e2) { return e1->renderOrder() < e2->renderOrder(); });
		for (auto& entity : s_entities) {
			if (RenderOrder::ZhicheAsLast == entity->renderOrder()) {
				renderZhiche(entity);
				continue;
			}
			entity->render();
		}

		GLState::flush();

		GLState::bindDefault();

		swapBuffer();

		return s_frontFBO;
	}

private:
	Scene() = default;

	/**@note swap buffers manually */
	static void swapBuffer() {
		if (!s_frontFBO) return;
		auto temp = s_frontFBO;
		s_frontFBO = s_backFBO;
		s_backFBO = temp;
	}

	static bool s_isInitialized;
	static bool s_isRendering;
	static CList<Entity*> s_entities;

	static FBO *s_backFBO, *s_frontFBO;
};